package org.stars.datastructure.map;


import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Hash map which keys are case insensitive.
 * 
 * @author Francesco Benincasa
 *
 * @param <K>
 */
public class CaseInsensitiveHashMap<K> extends HashMap<String, K> {

	/**
	 * The internal mapping from lowercase keys to the real keys.
	 * <p>
	 * Any query operation using the key ({@link #get(Object)},
	 * {@link #containsKey(Object)}) is done in three steps:
	 * <ul>
	 * <li>convert the parameter key to lower case</li>
	 * <li>get the actual key that corresponds to the lower case key</li>
	 * <li>query the map with the actual key</li>
	 * </ul>
	 * </p>
	 */
	private final Map<String, String>	lowerCaseMap		= new HashMap<String, String>();

	/**
	 * 
	 * Required for serialization support.
	 * 
	 * 
	 * 
	 * @see java.io.Serializable
	 */

	private static final long			serialVersionUID	= 1841673097701957808L;

	@Override
	public boolean containsKey(Object key) {
		if (key==null) return false;
		
		return super.containsKey(lowerCaseMap.get(key.toString().toLowerCase()));
	}

	/**
	 * 
	 * @see java.util.Map#get(java.lang.Object)
	 */
	@Override
	public K get(Object key) {
		if (key==null) return null;

		return super.get(lowerCaseMap.get(key.toString().toLowerCase()));

	}

	/* (non-Javadoc)
	 * @see java.util.HashMap#put(java.lang.Object, java.lang.Object)
	 */
	@Override
	public K put(String key, K value) {
		/*
		 * 
		 * In order to keep the map and lowerCaseMap synchronized,
		 * we have to remove the old mapping before putting the
		 * new one. Indeed, oldKey and key are not necessaliry equals.
		 * (That's why we call super.remove(oldKey) and not just
		 * super.put(key, value))
		 */
		String oldKey = lowerCaseMap.put(key.toLowerCase(), key);

		K oldValue=null;
		
		if (oldKey!=null)
		{
			oldValue = super.remove(oldKey);
		}
		
		super.put(key, value);

		return oldValue;
	}

	
	/* (non-Javadoc)
	 * @see java.util.HashMap#putAll(java.util.Map)
	 */
	@Override
	public void putAll(Map<? extends String, ? extends K> m) {		
		super.putAll(m);
		
		for(Entry<? extends String, ? extends K> item: m.entrySet())
		{
			put(item.getKey(), item.getValue());
		}
	}
				
	/**
	 * 
	 * @see java.util.Map#remove(java.lang.Object)
	 */

	@Override
	public K remove(Object key) {
		if (key==null) return null;
		return super.remove(lowerCaseMap.remove(key.toString().toLowerCase()));

	}

	/* (non-Javadoc)
	 * @see java.util.HashMap#clear()
	 */
	@Override
	public void clear() {
		lowerCaseMap.clear();
		
		super.clear();
	}
}
